home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / ARGONET / PD / SOUND / REPLAYER.SPK / c / command next >
Text File  |  1998-09-04  |  15KB  |  460 lines

  1.  
  2. /* command.c
  3.  
  4.    Replayer -- audio player
  5.    Copyright (c) 1997/8 Mark Seaborn <mseaborn@argonet.co.uk>
  6.  
  7.    This source file provides a quick command line front end to Replayer.
  8.    It has some diagnostic features to print out the headers of a Replay
  9.    file.  This is all portable, but RISC OS-specific features can be
  10.    enabled by defining `RISCOS' (enables TaskWindow idling, interactive
  11.    use).
  12. */
  13.  
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include "Dreamscape:bool.h"
  18.  
  19. #ifdef RISCOS
  20. #include "OS:osbyte.h"
  21. #include "OS:taskwindow.h"
  22. #endif
  23.  
  24. #ifdef MemCheck_MEMCHECK
  25. #include "MemCheck:MemCheck.h"
  26. #endif
  27.  
  28. #include "Replayer:replayer.h"
  29.  
  30.  
  31. /* Declarations */
  32.  
  33. #define tool    "replayer"
  34.  
  35. static void usage(void);
  36.  
  37. static void print_time(int *length, const replayer_file *file);
  38. static void remove_time(int *length);
  39. static char *time_string(int time);
  40.  
  41. static void print_replay_header(FILE *file, const replayer_header *header);
  42. static void print_replay_chunk_catalogue(FILE *file,
  43.     const replayer_chunk_entry *chunks, int no_chunks, int soundtracks);
  44.  
  45.  
  46. /* Main functions */
  47.  
  48. /* Prints Replayer's little help message. */
  49. static void usage(void)
  50. {
  51.   fprintf(stderr, "Usage:  %s [options] [--] filenames...\n\n"
  52.     "Where available options are:\n\n"
  53.     "  -d, --diagnostics  Print file header info as a diagnostic\n"
  54.     "  -p, --play         Plays the sound part of the Replay file\n"
  55.     "  -v, --verbose      Print status messages\n"
  56.     "  -q, --quality n    Sound quality, ranging from 1--4 (default 4)\n"
  57.     "  -l, --loop         Play the list of files repeatedly\n"
  58.     "  -t, --timer        Display timer\n"
  59. #ifndef replayer_NO_BACKWARDS
  60.     "  -b, --backwards    Play sound backwards\n"
  61. #endif
  62. #ifdef RISCOS
  63.     "  -i, --interactive  Interactive mode; accept keypresses\n"
  64. #endif
  65.     "  -h, --help         Show this message\n"
  66.     "  filenames...       The name(s) of the file(s) to play\n"
  67.     "\n", tool);
  68. }
  69.  
  70. /* The main part of the command line tool. */
  71. int main(int argc, char *argv[])
  72. {
  73.   /* Some of the variables we need. */
  74.   bool play = 0, diagnostics = 0, verbose = 0, loop = 0, timer = 0;
  75.   int quality = 4;
  76. #ifndef replayer_NO_BACKWARDS
  77.   bool backwards = 0;
  78. #endif
  79. #ifdef RISCOS
  80.   bool interactive = 0,
  81.        muted = 0; /* Preserve mute state between files. */
  82. #endif
  83.   int i, /* Misc counter. */
  84.       first_file_arg = argc, /* First non-switch argument. */
  85.       next_arg; /* Next argument to use as filename. */
  86.  
  87.   /* Turn MemCheck on if it's there. */
  88. #ifdef MemCheck_MEMCHECK
  89.   MemCheck_Init();
  90.   MemCheck_RegisterArgs(argc, argv);
  91.   MemCheck_InterceptSCLStringFunctions();
  92.   MemCheck_SetStoreMallocFunctions(1);
  93. #endif
  94.  
  95.   /* Parse the arguments we're given. */
  96.   for(i=1; i<argc; ++i) {
  97.     if(argv[i][0] == '-' && argv[i][1]) {
  98.       const char *c;
  99.  
  100.       /* Long options, ie. `--foo'. */
  101.       if(argv[i][1] == '-' && argv[i][2]) {
  102.         const char *arg = argv[i] + 2;
  103.         if(!strcmp(arg, "diagnostics")) { diagnostics = 1; continue; }
  104.         if(!strcmp(arg, "play")) { play = 1; continue; }
  105.         if(!strcmp(arg, "verbose")) { verbose = 1; continue; }
  106.         if(!strcmp(arg, "quality")) { quality = atoi(argv[++i]); continue; }
  107.         if(!strcmp(arg, "loop")) { loop = 1; continue; }
  108.         if(!strcmp(arg, "timer")) { timer = 1; continue; }
  109. #ifndef replayer_NO_BACKWARDS
  110.         if(!strcmp(arg, "backwards")) { backwards = 1; continue; }
  111. #endif
  112. #ifdef RISCOS
  113.         if(!strcmp(arg, "interactive")) { interactive = 1; continue; }
  114. #endif
  115.         if(!strcmp(arg, "help")) { usage(); return EXIT_SUCCESS; }
  116.         fprintf(stderr, "%s: Unknown argument, `%s'\n", tool, argv[i]);
  117.         return EXIT_FAILURE;
  118.       }
  119.       /* Short options, ie. `-f -v' or combined, `-fv'. */
  120.       if(argv[i][1] != '-') {
  121.         for(c=argv[i]+1; *c; ++c) switch(*c) {
  122.           case 'd':  diagnostics = 1; continue;
  123.           case 'p':  play = 1; continue;
  124.           case 'v':  verbose = 1; continue;
  125.           case 'q':  quality = atoi(argv[++i]); continue;
  126.           case 'l':  loop = 1; continue;
  127.           case 't':  timer = 1; continue;
  128. #ifndef replayer_NO_BACKWARDS
  129.           case 'b':  backwards = 1; continue;
  130. #endif
  131. #ifdef RISCOS
  132.           case 'i':  interactive = 1; continue;
  133. #endif
  134.           case 'h':  usage(); return EXIT_SUCCESS;
  135.           default:
  136.             fprintf(stderr, "%s: Unknown argument, `-%c'\n", tool, *c);
  137.             return EXIT_FAILURE;
  138.         }
  139.         continue;
  140.       }
  141.     }
  142.     /* The rest are non-switch arguments. */
  143.     first_file_arg = i;
  144.     /* If this is the switch terminator `--', move on one. */
  145.     if(argv[i][0] == '-' && argv[i][1] == '-' && !argv[i][2])
  146.       ++first_file_arg;
  147.     break;
  148.   }
  149.  
  150.   if(verbose) printf("%s: Starting...\n", tool);
  151.  
  152.   /* Complain if no filename was passed. */
  153.   if(first_file_arg >= argc) {
  154.     fprintf(stderr, "%s: You must supply at least one filename "
  155.     "of a Replay file.\n\n", tool);
  156.     usage();
  157.     return EXIT_FAILURE;
  158.   }
  159.   /* Check quality value is valid. */
  160.   if(quality < 1 || quality > 4) {
  161.     fprintf(stderr, "%s: Quality value %i invalid (must be 1--4)\n",
  162.     tool, quality);
  163.     return EXIT_FAILURE;
  164.   }
  165.   /* If no options were set, default to playing. */
  166.   if(!play && !diagnostics) play = 1;
  167.  
  168. #ifdef RISCOS
  169.   if(interactive) printf("%s: Interactive mode; press `h' for help\n", tool);
  170. #endif
  171.  
  172.   /* Deal with each file in turn. */
  173.   next_arg = first_file_arg;
  174.   while(next_arg < argc) {
  175.     replayer_file *file;
  176.     bool finish_loop = 0;
  177.     const char *filename = argv[next_arg++];
  178.  
  179.     /* Open the Replay file. */
  180.     file = replayer_create_file(filename);
  181.     if(!file) {
  182.       fprintf(stderr, "%s: Couldn't open Replay file `%s'\n",
  183.         tool, filename);
  184.       return EXIT_FAILURE;
  185.     }
  186.  
  187.     /* Print diagnostics. */
  188.     if(diagnostics) {
  189.       const replayer_header *header;
  190.       const replayer_chunk_entry *chunks;
  191.       header = replayer_get_header(file);
  192.  
  193.       if(header) {
  194.         if(verbose) printf("%s: Header read successfully\n", tool);
  195.         print_replay_header(stdout, header);
  196.         chunks = replayer_get_chunk_catalogue(file);
  197.         if(!chunks)
  198.         fprintf(stderr, "%s: Couldn't get chunk catalogue\n", tool);
  199.           else print_replay_chunk_catalogue(stdout, chunks,
  200.         header->no_chunks, header->no_soundtracks);
  201.       }
  202.       else { fprintf(stderr, "No good header\n"); }
  203.     }
  204.  
  205.     /* Play the file, sound only. */
  206.     if(play) {
  207.       int rc, time_state = timer ? 0 : -1;
  208.  
  209.       if(verbose) {
  210.         int len_cs = replayer_get_length(file);
  211.         char *len_str = len_cs > 0 ? time_string(len_cs) : 0;
  212.         printf("%s: Playing file %i, `%s' (length %s)\n", tool,
  213.         next_arg - first_file_arg, filename,
  214.         len_str ? len_str : "?");
  215.         free(len_str);
  216.       }
  217.       /* Set options. */
  218.       replayer_set_sound_quality(file, quality);
  219. #ifndef replayer_NO_BACKWARDS
  220.       replayer_set_backwards(file, backwards);
  221. #endif
  222.  
  223.       /* Start playing. */
  224.       rc = replayer_sound_play(file, 0);
  225.       if(rc) {
  226.         fprintf(stderr, "%s: Playback failed (code %i)\n", tool, rc);
  227.         goto finish;
  228.       }
  229.       replayer_sound_set_mute(file, muted); /* Preserve mute state. */
  230.       while(1) {
  231.         int code;
  232. #ifdef RISCOS
  233.         bool tw;
  234.         print_time(&time_state, file);
  235.         /* See if we're running in a TaskWindow. */
  236.         if(!xtaskwindowtaskinfo_window_task(&tw) && tw) {
  237.           /* The TaskWindow can let us sleep until a pollword becomes
  238.              non-zero.  This is an under-used feature of the TaskWindow!
  239.              Hardly surprising when the documentation is in the wrong
  240.              section and is gibberish. */
  241.           const int *poll_word = replayer_get_poll_word(file);
  242.           if(poll_word) xupcall_sleep((int *) poll_word, 0);
  243.           /* This does work:  It reduces time wasted in this loop and
  244.              improves desktop responsiveness. */
  245.         }
  246.         /* See if there is a keypress waiting. */
  247.         if(interactive) {
  248.           int key, key_unread;
  249.           if(!xos_byte(129, 0, 0, &key, &key_unread) && !key_unread) {
  250.             remove_time(&time_state);
  251.             switch(key) {
  252.               case 'p': case 'P': {
  253.                 bool paused = !replayer_sound_get_pause(file);
  254.                 if(replayer_sound_set_pause(file, paused))
  255.                   printf("Couldn't pause\n");
  256.                   else printf(paused ? "Paused\n" : "Unpaused\n");
  257.                 break;
  258.               }
  259.               case 'm': case 'M': {
  260.                 /* Mute state is preserved between files (pause is not). */
  261.                 muted = !muted;
  262.                 if(replayer_sound_set_mute(file, muted))
  263.                   printf("Couldn't mute\n");
  264.                   else printf(muted ? "Muted\n" : "Unmuted\n");
  265.                 break;
  266.               }
  267.               case 'n': case 'N':  goto finish;
  268.               case 'c': case 'C': {
  269.                 char buffer[10], *c = buffer;
  270.                 int ch, no;
  271.                 printf("Change to nth file in list: ");
  272.                 /* Read number from stdin. */
  273.                 while(ch = getchar(), (ch != EOF && ch != '\n')) {
  274.                   if(c >= (buffer + sizeof(buffer) - 1)) break;
  275.                   *c++ = ch;
  276.                 }
  277.                 *c = 0;
  278.                 no = atoi(buffer);
  279.                 if(!no) break; /* Ignore if number was zero. */
  280.                 next_arg = first_file_arg + no - 1;
  281.                 goto finish;
  282.               }
  283.               case 'q': case 'Q':  finish_loop = 1; goto finish;
  284.               case 'h': case 'H':
  285.                 printf("\nAvailable keys are:\n\n"
  286.             "  p  Pause (and unpause) playback\n"
  287.             "  m  Mute (and unmute) playback\n"
  288.             "  n  End the current file (and start the next)\n"
  289.             "  q  End all files and quit\n"
  290.             "  c  Change file (prompts number)\n"
  291.             "  h  Show this message\n"
  292.             "\n");
  293.                 break;
  294.             }
  295.           }
  296.         }
  297. #else
  298.         print_time(&time_state, file);
  299. #endif
  300.         code = replayer_sound_feed(file);
  301.         if(code > 0) {
  302.           remove_time(&time_state);
  303.           fprintf(stderr, "%s: Sound playback failed with error (%i)\n",
  304.         tool, code);
  305.         break;
  306.         }
  307.         if(code == replayer_feed_FINISHED) break;
  308.       }
  309.       remove_time(&time_state);
  310.     }
  311.  
  312.   finish:
  313.     /* Destroy the Replay object and finish. */
  314.     replayer_destroy(file);
  315.     if(finish_loop) break;
  316.     if(loop && next_arg >= argc) next_arg = first_file_arg;
  317.   }
  318.  
  319.   if(verbose) printf("%s: Finished\n", tool);
  320.  
  321.   /* If you want, you can uncomment the next line to force MemCheck to
  322.      list blocks, and check for a memory leak */
  323. #ifdef MemCheck_MEMCHECK
  324.   MemCheck_OutputBlocksInfo();
  325. #endif
  326.  
  327.   return EXIT_SUCCESS;
  328. }
  329.  
  330. /* Prints the current time position. */
  331. static void print_time(int *length, const replayer_file *file)
  332. {
  333.   if(*length != -1) {
  334.     int time = replayer_sound_get_time(file);
  335.     if(*length) remove_time(length);
  336.     *length = printf("  %i:%c%c:%c%c  ",
  337.     time / (60 * 100),        /* Minutes */
  338.     (time / 1000) % 6 + '0',    /* Seconds big digit */
  339.     (time / 100) % 10 + '0',    /* Seconds small digit */
  340.     (time / 10) % 10 + '0',        /* Centiseconds big digit */
  341.     time % 10 + '0');        /* Centiseconds small digit */
  342.     if(*length < 0) *length = 0;
  343.   }
  344. }
  345.  
  346. /* Removes the previously-printed time. */
  347. static void remove_time(int *length)
  348. {
  349. #ifdef RISCOS
  350.   if(*length > 0) {
  351.     while((*length)--) os_delete();
  352.     *length = 0;
  353.   }
  354. #else
  355.   printf("\n");
  356. #endif
  357. }
  358.  
  359. /* Given a time in centiseconds, returns a string of the time in the form
  360.    `(MM)M:SS:CC'.  Returns 0 for failure.  Free the string yourself. */
  361. static char *time_string(int time)
  362. {
  363.   char *s = malloc(12);
  364.   if(!s) return 0;
  365.   sprintf(s, "%i:%c%c:%c%c",
  366.     time / (60 * 100),        /* Minutes */
  367.     (time / 1000) % 6 + '0',    /* Seconds big digit */
  368.     (time / 100) % 10 + '0',    /* Seconds small digit */
  369.     (time / 10) % 10 + '0',        /* Centiseconds big digit */
  370.     time % 10 + '0');        /* Centiseconds small digit */
  371.   return s;
  372. }
  373.  
  374.  
  375. /* Diagnostic functions */
  376.  
  377. /* This function prints the information in the Replay header to the
  378.    stream you give it. */
  379. static void print_replay_header(FILE *file, const replayer_header *header)
  380. {
  381.   int i;
  382.   fprintf(file, "\nARMovie header:\n");
  383.  
  384.   fprintf(file, "\nMisc --\n");
  385.   fprintf(file, "Title: `%s'\n", header->title);
  386.   fprintf(file, "Date & (c): `%s'\n", header->date_and_copyright);
  387.   fprintf(file, "Other: `%s'\n", header->other_details);
  388.  
  389.   fprintf(file, "\nVideo --\n");
  390.   fprintf(file, "Video type: `%s'\n", header->video_type);
  391.   fprintf(file, "Dimensions: %i x %i pixels\n",
  392.         header->width, header->height);
  393.   fprintf(file, "Colour depth: %i bpp\n", header->colour_depth);
  394.   fprintf(file, "Frames/sec: %i\n", header->fps);
  395.  
  396.   fprintf(file, "\nSound --\n");
  397.   if(!header->no_soundtracks) fprintf(file, "(No soundtracks)\n");
  398.   for(i=0; i<header->no_soundtracks; ++i) {
  399.     char *driver;
  400.     fprintf(file, "%i.\n"
  401.     "Type: `%s'\n"
  402.     "Sample rate: %f Hz\n"
  403.     "Channels: %i\n"
  404.     "Precision: %i bits\n",
  405.     i + 1,
  406.     header->soundtracks[i].type,
  407.     header->soundtracks[i].rate,
  408.     header->soundtracks[i].channels,
  409.     header->soundtracks[i].precision);
  410.     fprintf(file, "Reverse channels (%i), linear (%i), unsigned (%i)\n",
  411.     header->soundtracks[i].reverse_channels,
  412.     header->soundtracks[i].linear_sound,
  413.     header->soundtracks[i].unsigned_sound);
  414.     driver = replayer_get_driver_filename(&header->soundtracks[i]);
  415.     if(driver) {
  416.       fprintf(file, "[Driver: `%s']\n", driver);
  417.       free(driver);
  418.     }
  419.     else { fprintf(file, "[Could not find driver]\n"); }
  420.   }
  421.  
  422.   fprintf(file, "\nTiming and other --\n");
  423.   fprintf(file, "Chunks: %i\n", header->no_chunks);
  424.   fprintf(file, "Chunk catalogue: %i (offset)\n", header->chunk_catalogue);
  425.   fprintf(file, "Frames/chunk: %i\n", header->frames_per_chunk);
  426.   fprintf(file, "Even chunk size: %i\n", header->even_chunk_size);
  427.   fprintf(file, "Odd chunk size: %i\n", header->odd_chunk_size);
  428.   fprintf(file, "Sprite: %i (offset), %i (size)\n",
  429.     header->sprite_offset, header->sprite_size);
  430.   fprintf(file, "Key frames: %i (offset)\n", header->key_frames_offset);
  431. }
  432.  
  433. /* Prints the Replay chunk catalogue given to the stream given.
  434.    You must pass the number of chunks (minus one, as given in the header)
  435.    and the number of soundtracks yourself, because these are given in the
  436.    header, not the catalogue itself. */
  437. static void print_replay_chunk_catalogue(FILE *file,
  438.     const replayer_chunk_entry *chunks, int no_chunks, int soundtracks)
  439. {
  440.   int i = 0, j;
  441.   const replayer_chunk_entry *c = chunks,
  442.      *end = (const replayer_chunk_entry *) ((const char *) chunks +
  443.         replayer_chunk_entry_SIZE(soundtracks) * (no_chunks + 1));
  444.  
  445.   fprintf(file, "\nARMovie chunk catalogue:\n\n");
  446.   while(c < end) {
  447.     fprintf(file, "%i. "
  448.     "Offset: %i, "
  449.     "video size: %i",
  450.     i, c->offset, c->video_size);
  451.     for(j=0; j<soundtracks; ++j)
  452.       { fprintf(file, ", sound#%i size: %i", j+1, c->sound_size[j]); }
  453.     fprintf(file, "\n");
  454.  
  455.     ++i;
  456.     c = (const replayer_chunk_entry *) ((const char *) c +
  457.     replayer_chunk_entry_SIZE(soundtracks));
  458.   }
  459. }
  460.